All posts
Engineering 7 min read

Why every section is plain Blade

We could have stored pages in a database and rendered them from JSON. We chose files you can open in your editor, and it has paid for itself every week since.

Marcus Bell

Co-founder and CTO

A stack of plain white index cards held by a brass binder clip on an oak desk

The first version of Starter kept pages in a database. Every section was a row, every field a JSON blob, and a renderer stitched them together on each request. It demoed well. It also meant that the moment a developer wanted to change a section, they had to leave their editor, open ours, and work through a form that knew less about HTML than they did.

We threw it out in the spring of 2025. Since then, every section in Starter is an ordinary anonymous Blade component with a small YAML file beside it. The page you edit on the canvas is a file in resources/views/pages. The canvas reads it, and when you publish, it writes it back.

What a section actually is

A section is two files. The Blade file declares its props with defaults, and renders them. The YAML file names the same props, says what kind of field each one is, and repeats the defaults. That is the whole contract.

@props(['heading' => 'Plans that grow with you'])
<h2 class="text-h2">{{ $heading }}</h2>

Starter does not annotate anything. It reads the YAML to build the inspector, and it reads the Blade to find where each field is echoed, so clicking the heading on the canvas puts a caret in the right place. If you write a new section by hand and give it a YAML file, it shows up in the Add Section picker on the next page load.

Why files won

Three things decided it for us, and none of them were about performance.

  • Review. A content change is a diff. When a marketer changes a headline, the pull request shows one attribute on one line. When a developer changes the layout of the pricing table, it shows exactly that. Nobody has to trust a dashboard’s history view.
  • Removal. If you uninstall Starter tomorrow, your site keeps serving. The runtime is one service provider that lives in your app, and it has no dependency on the editor. We think a tool that holds your pages hostage is a tool you should not adopt.
  • Everything else you already use. Blade components can call other components, read config, use @php blocks and loops. Tailwind sees the classes. Your formatter formats them. Your deploy deploys them. We did not have to build a single one of those things.

The cost

Files are harder for us than rows. The writer that turns an edit back into a page file has to change one attribute and leave every other byte alone, including your comments and your blank lines. It took Owen most of last winter to make that true across all forty templates we test against, and the test that guards it still runs on every commit: install a template, write every page back without changes, and fail if a single byte moved.

The other cost is that some things a database makes easy, like full-text search across pages, are slower to build. We decided that was a fine trade for a product whose job is a marketing site of a few dozen pages.

What it means for your team

In practice, the split is clean. Developers own the sections: their markup, their props, which fields exist. Editors own the values: the words, the images, the order of sections on a page. Neither waits on the other, and both of their changes land in the same repository.

If you have a section library already, the fastest way to try this is to add a YAML file next to one component you like and open the page in Starter. You will see its fields in the inspector in about the time it took to read this paragraph.

One email a month

What shipped, what changed, and one marketing page worth stealing from another team. That is all.

No spam. Unsubscribe any time.